djinnifandomcom-20200222-history
Animations in Scripting
Category:Scripts Two ways of firing animations PlayAnimation() function Using the PlayAnimation() function, they can be fired instantly regardless of context // Play nAnimation immediately. // - nAnimation: ANIMATION_* // - fSpeed // - fSeconds void PlayAnimation(int nAnimation, float fSpeed=1.0, float fSeconds=0.0); ActionPlayAnimation() function Alternatively, we can use the ActionPlayAnimation() function. It will queue the animation and fire it at its turn. (see Queue in Scripting tutorial) // Cause the action subject to play an animation //-nAnimation: ANIMATION_* // -fSpeed: Speed of the animation // -fDurationSeconds: Duration of the animation (this is not used for Fire and // Forget animations) void ActionPlayAnimation(int nAnimation, float fSpeed=1.0, float fDurationSeconds=0.0); Assignement to target The best way to use animations is to physically assign them to the desired target using the AssignCommand() function: AssignCommand(OTARGET,ActionPlayAnimation(ANIMATION_*)); Animation Constants ANIMATION_* (as seen in the example above) are constants defined in nwscriptdefn.nss which can be found in the “The Witcher\System\Scripts” directory. Although this file contains many constants and functions usable through the Djinni editor, there is only a relatively short list of Animation constants. // Looping animation constants. int ANIMATION_LOOPING_PAUSE = 0; int ANIMATION_LOOPING_PAUSE2 = 1; int ANIMATION_LOOPING_LISTEN = 2; int ANIMATION_LOOPING_MEDITATE = 3; int ANIMATION_LOOPING_WORSHIP = 4; int ANIMATION_LOOPING_LOOK_FAR = 5; int ANIMATION_LOOPING_SIT_CHAIR = 6; int ANIMATION_LOOPING_SIT_CROSS = 7; int ANIMATION_LOOPING_TALK_NORMAL = 8; int ANIMATION_LOOPING_TALK_PLEADING = 9; int ANIMATION_LOOPING_TALK_FORCEFUL = 10; int ANIMATION_LOOPING_TALK_LAUGHING = 11; int ANIMATION_LOOPING_GET_LOW = 12; int ANIMATION_LOOPING_GET_MID = 13; int ANIMATION_LOOPING_PAUSE_TIRED = 14; int ANIMATION_LOOPING_PAUSE_DRUNK = 15; int ANIMATION_LOOPING_DEAD_FRONT = 16; int ANIMATION_LOOPING_DEAD_BACK = 17; int ANIMATION_LOOPING_CONJURE1 = 18; int ANIMATION_LOOPING_CONJURE2 = 19; int ANIMATION_LOOPING_SPASM = 20; int ANIMATION_LOOPING_WORK1 = 21; int ANIMATION_LOOPING_WORK2 = 22; int ANIMATION_LOOPING_WORK3 = 23; int ANIMATION_LOOPING_WORK4 = 24; int ANIMATION_LOOPING_WORK5 = 25; int ANIMATION_LOOPING_POSE_CALM = 26; int ANIMATION_LOOPING_POSE_CHEER = 27; int ANIMATION_LOOPING_POSE_EXCITED = 28; int ANIMATION_LOOPING_POSE_NERVOUS = 29; int ANIMATION_LOOPING_GEST_TALK = 30; int ANIMATION_LOOPING_GEST_SPEECH = 31; int ANIMATION_LOOPING_GEST_MONOLOGUE = 32; int ANIMATION_LOOPING_GEST_AMUSED = 33; int ANIMATION_LOOPING_GEST_TIRED = 34; int ANIMATION_LOOPING_GEST_BORED = 35; int ANIMATION_LOOPING_GEST_THREAT = 36; int ANIMATION_LOOPING_GEST_RETREAT = 37; int ANIMATION_LOOPING_GEST_ASSURE = 38; int ANIMATION_LOOPING_GEST_RESOLUTE = 39; int ANIMATION_LOOPING_GEST_JOKE = 40; // Fire and forget animation constants. int ANIMATION_FIREFORGET_HEAD_TURN_LEFT = 100; int ANIMATION_FIREFORGET_HEAD_TURN_RIGHT = 101; int ANIMATION_FIREFORGET_PAUSE_SCRATCH_HEAD = 102; int ANIMATION_FIREFORGET_PAUSE_BORED = 103; int ANIMATION_FIREFORGET_SALUTE = 104; int ANIMATION_FIREFORGET_BOW = 105; int ANIMATION_FIREFORGET_STEAL = 106; int ANIMATION_FIREFORGET_GREETING = 107; int ANIMATION_FIREFORGET_VICTORY1 = 109; int ANIMATION_FIREFORGET_VICTORY2 = 110; int ANIMATION_FIREFORGET_VICTORY3 = 111; int ANIMATION_FIREFORGET_READ = 112; int ANIMATION_FIREFORGET_DRINK = 113; int ANIMATION_FIREFORGET_DODGE_SIDE = 114; int ANIMATION_FIREFORGET_DODGE_DUCK = 115; int ANIMATION_FIREFORGET_GEST_BOW = 116; int ANIMATION_FIREFORGET_GEST_YAWN = 117; int ANIMATION_FIREFORGET_GEST_INVITE = 118; int ANIMATION_FIREFORGET_GEST_SALUTE = 119; int ANIMATION_FIREFORGET_TELEPORT_PRE = 120; int ANIMATION_FIREFORGET_TELEPORT_POST = 121; int ANIMATION_FIREFORGET_SPECIAL1 = 122; int ANIMATION_FIREFORGET_SPECIAL2 = 123; int ANIMATION_FIREFORGET_SPECIAL3 = 124; int ANIMATION_FIREFORGET_SPECIAL4 = 125; int ANIMATION_ELIXIR_7 = 126; int ANIMATION_VANISH = 127; int ANIMATION_FIREFORGET_ELIXIR_EFFECT = 174; // Placeable animation constants int ANIMATION_PLACEABLE_ACTIVATE = 200; int ANIMATION_PLACEABLE_DEACTIVATE = 201; int ANIMATION_PLACEABLE_OPEN = 202; int ANIMATION_PLACEABLE_CLOSE = 203; int ANIMATION_PLACEABLE_DEAD = 204; int ANIMATION_DOOR_OPEN1 = 300; int ANIMATION_DOOR_OPEN2 = 301; int ANIMATION_DOOR_CLOSED = 302; Compared to the classic NWN definition script, where 200 or more animations are listed as “common” for any given character, this is pretty short! Unfortunately, the reason is because it’s not complete, there is a logic hidden behind. Basic character embeded animations If you want to use an animation that is not mentioned in this list, there is a trick. First, make sure this animation exists in the Geralt’s character model for example (see screenshot). Questions and hypothesis This left us with a question: *How do we link these character embedded animations to a scriptable animation? To answer this question we spent a lot of time learning the engine itself. Let's enlight a few things: *First of all, by invoking animation with constants not defined into nwdefscript.nss, we did not find any hidden value that lead to an animation—but there could be some that we did not yet find. *We inspected the overall functioning of 2da files in order to understand how some animation can be fired avoiding constants. (See the spell section for more detail). *We can make our own animations by filling the following “empty”, but defined, slots. ANIMATION_FIREFORGET_SPECIAL1-5 *We have tested a lot of different functions related to animations and effects. Animation and effect type are defined into the Djinni but not as a common type (like float or int as it is in NeverWinter Nights). In addition, effects do not work like animations because there are special dedicated functions that can return an effect based on constants. *For example let's consider this code: effect efly; eFly = EffectVisualEffect(EFFECT_TYPE_APPLY_RUNNING_EFFECT,FALSE); eFly = EffectDisappearAppear(lLocTarget); eFly = EffectAppear(); This does not exist for animations. We have come to a conclusion concerning this: *There have been a lot of cuts and changes in the data and structure management between NWN and The Witcher. In The Witcher, data is not identically accessible, and most of it is hidden in the engine and not accessible via scripting at all. We suppose then that animations are stored into meshes, and these meshes contain only engine or basic animations that are NOT usable in scripting. We think then that somewhere above these animations some explicitly usable ones are defined (accessible through constants)—they may be combinations of basic animations. It is a kind of facade, or layer to hide the whole content of animations from scripters. This is probably there due to the specificity of character's animations that would not be applicable to all elements. (Different nodes and so on...) Spell Casting : How it works. Spell casting is hugely different from the way it was used in NWN. No specific constant is defined like in NWN—the system is a little bit more complex. A few different functions exist that, here’s one: // The action subject will fake casting a spell at oTarget; the conjure and cast // animations and visuals will occur, nothing else. // - nSpell // - oTarget // - nProjectilePathType: PROJECTILE_PATH_TYPE_* void ActionCastFakeSpellAtObject(int nSpell, object oTarget, int nProjectilePathType=PROJECTILE_PATH_TYPE_DEFAULT); The functions still takes an integer as to determine which spell to cast. However, this integer does not directly refer to a constant, but more to row into a 2da file. The spells.2da contains a lot of additional information about spells and the way to cast them. Let's have a look at it (screenshot): You can see a given spell has more that one line. This is due to the level you can have in such a spell. Depending on the level some parameter will change (duration, power, visual effect, and so on) Also, you can see a column named "ImpactScript" which permits the scripter to fire a special script depending on the spell cast. This should take care of all the different things a spell should do (damage, stun and so on) As a reminder it is possible to fire a script by its name using the function ExecuteScript When it comes to animation the spell calls, there are two different columns: ConjAnim and CastAnim. They may be the keys to define the animation to fire when firing this spell. It seems to be done internally since I can’t get any trace of it anywhere... Perhaps some parsing is performed... Let's come back to firing through an inbuilt function. // SPELL_SUBTYPE_X There is a function to find “X”: int FindSpellByTypeAndLevel(string sType, int nSubtype, int nLevel); Here is a common use of this function in an existing nss script for AI : case COMBAT_HEAL: { DClearAllActions(FALSE, oSource); object oWounded = _GetWounded(oSource); int nSpell = FindSpellByTypeAndLevel("Heal", SPELL_SUBTYPE_NORMAL, 0); AssignCommand(oSource, ActionCastSpellAtObject(nSpell, oSource, TRUE)); _SetWounded(oSource, oSource); break; } from inc_ai.nss The first parameter may determine the spell to cast (HEAL, AARD, IGNI...). The second can be either SPELL_SUBTYPE_NORMAL or SPELL_SUBTYPE_ (SpellSubType) and the nLevel should refer to the level of the spell the caster has >> column spellLevel. Then it determines the correct line this way and fire the spell associated to it. That's it for spell casting.